var fs = require('fs')
var path = require('path')
var xml2json = require('xml2js').parseString;
var moment = require('moment');
const DT_FORMAT = 'YYYY-MM-DD HH:mm:ss'
let dateFormat = function (dt) {
  return moment(new Date(dt)).format(DT_FORMAT)
}

var admins = [],
  logs = [],
  players = [],
  raids = [],
  boardMsg = '';

! function () {
  let file = '';
  file = fs.readFileSync(path.join(__dirname, 'admins.json'), {
    encoding: 'utf8'
  });
  JSON.parse(file).forEach(o => {
    admins.push(o)
  })

  file = fs.readFileSync(path.join(__dirname, 'logs.json'), {
    encoding: 'utf8'
  });
  JSON.parse(file).forEach(o => {
    logs.push(o)
  })

  file = fs.readFileSync(path.join(__dirname, 'players.json'), {
    encoding: 'utf8'
  });
  JSON.parse(file).forEach(o => {
    o.scores.TAQ = o.scores.TAQ || 0;
    o.scores.NAXX = o.scores.NAXX || 0;
    o.scores.TAQ2 = o.scores.TAQ2 || 0;
    o.scores.NAXX2 = o.scores.NAXX2 || 0;
    players.push(o)
  })

  file = fs.readFileSync(path.join(__dirname, 'raids.json'), {
    encoding: 'utf8'
  });
  JSON.parse(file).forEach(o => {
    raids.push(o)
  })

  boardMsg = fs.readFileSync(path.join(__dirname, 'board.txt'), {
    encoding: 'utf8'
  });
}()

function mkdirsSync(dirpath, mode) {
  try {
    if (!fs.existsSync(dirpath)) {
      let pathtmp;
      dirpath.split(/[/\\]/).forEach(function (dirname) { //这里指用/ 或\ 都可以分隔目录  如  linux的/usr/local/services   和windows的 d:\temp\aaaa
        if (pathtmp) {
          pathtmp = path.join(pathtmp, dirname);
        } else {
          pathtmp = dirname;
        }
        if (!fs.existsSync(pathtmp)) {
          if (!fs.mkdirSync(pathtmp, mode)) {
            return false;
          }
        }
      });
    }
    return true;
  } catch (e) {
    console.log("create director fail! path=" + dirpath + " errorMsg:" + e);
    return false;
  }
}

let backup = function(){
  let date = new Date;
  let year = date.getFullYear(),
    month = ('0' + (date.getMonth() + 1)).slice(-2),
    day = ('0' + date.getDate()).slice(-2),
    hour = ('0' + date.getHours()).slice(-2),
    minute = ('0' + date.getMinutes()).slice(-2),
    second = ('0' + date.getSeconds()).slice(-2)
  let prefix = `${year+month+day+hour+minute+second}`
  let savePath = path.join(__dirname,'..' , 'public', 'dkpFiles', 'backups', prefix)
        
  if (!fs.existsSync(savePath)) {
    if (!fs.mkdirSync(savePath)) {
      fs.copyFileSync(path.join(__dirname, 'admins.json'), path.join(savePath, 'admins.json'))
      fs.copyFileSync(path.join(__dirname, 'logs.json'), path.join(savePath, 'logs.json'))
      fs.copyFileSync(path.join(__dirname, 'players.json'), path.join(savePath, 'players.json'))
      fs.copyFileSync(path.join(__dirname, 'raids.json'), path.join(savePath, 'raids.json'))
    }
  }
}

let save = function(withoutBackup) {
  try {
    if(!withoutBackup){
      backup();
    }

    fs.writeFileSync(path.join(__dirname, 'admins.json'), JSON.stringify(admins))
    fs.writeFileSync(path.join(__dirname, 'logs.json'), JSON.stringify(logs))
    fs.writeFileSync(path.join(__dirname, 'players.json'), JSON.stringify(players))
    fs.writeFileSync(path.join(__dirname, 'raids.json'), JSON.stringify(raids))
  } catch (e) {
    console.log(e)
  }
}

let backupList = function(pageNo, pageSize) {
  let folders,total = 0;
  try{
    let folderPath = path.join(__dirname, '..', 'public', 'dkpFiles', 'backups');
    let fd = fs.readdirSync(folderPath);
    fd.reverse();
    folders = fd.slice((pageNo - 1) * pageSize, pageNo * pageSize).map(o=>({name: o}));
    total = fd.length;
  }catch(e){
  }
  return {
    total,
    list: folders
  };
}

let restore = function(restorePoint){
    let folderPath = path.join(__dirname, '..', 'public', 'dkpFiles', 'backups', restorePoint);
    let file = '';
    file = fs.readFileSync(path.join(folderPath, 'admins.json'), {
      encoding: 'utf8'
    });
    let oldAdmins = admins.concat();
    admins = [];
    JSON.parse(file).forEach(o => {
      let adm = oldAdmins.find(ad => ad.username == o.username)
      //保持token，避免当前登录用户被注销登录
      if(adm){
        o.token = adm.token;
      }
      admins.push(o)
    })

    file = fs.readFileSync(path.join(folderPath, 'logs.json'), {
      encoding: 'utf8'
    });
    logs = [];
    JSON.parse(file).forEach(o => {
      logs.push(o)
    })

    file = fs.readFileSync(path.join(folderPath, 'players.json'), {
      encoding: 'utf8'
    });
    players = [];
    JSON.parse(file).forEach(o => {
      players.push(o)
    })

    file = fs.readFileSync(path.join(folderPath, 'raids.json'), {
      encoding: 'utf8'
    });
    raids = [];
    JSON.parse(file).forEach(o => {
      raids.push(o)
    })

    save(true);
}

let adminDao = {
  add(admin) {
    return new Promise((resolve, reject) => {
      if (admins.some(o => o.username == admin.username)) {
        reject({
          code: 1005,
          msg: '账号已存在'
        });
      } else {
        admins.push(admin);
        resolve({
          code: 0
        })
      }
    })
  },
  update(admin) {
    return new Promise((resolve, reject) => {
      for (let i = 0; i < admins.length; i++) {
        if (admins[i].username == admin.username) {
          admins[i] = {
            ...admin
          };
          resolve({
            code: 0,
            msg: '操作成功',
            data: admin
          });
          return;
        }
      }
      reject({
        code: 1001,
        msg: '没有找到该用户'
      });
    })
  },
  delete(username) {
    return new Promise((resolve, reject) => {
      for (let i = 0; i < admins.length; i++) {
        if (admins[i].username == username) {
          admins.splice(i, 1);
          resolve({
            code: 0,
            msg: '操作成功'
          });
          return;
        }
      }
      reject({
        code: 1003,
        msg: '没有找到该用户'
      });
    })
  },
  check(username, password) {
    return admins.filter(o => o.username == username && o.password == password)[0]
  },
  find(username){
    return admins.find(o=>o.username==username);
  },
  search({
    username,
    level,
    pageNo = 1,
    pageSize = 10,
    sort,
    order
  }) {
    return new Promise((resolve, reject) => {
      let result = admins.filter(o => {
        return (!username || o.username == username) && (!level || o.level == level)
      })
      if (sort && order) {
        result = result.sort((a, b) => {
          if (order == '' || order == 'asc') {
            return a[sort] > b[sort] ? 1 : -1
          } else {
            return a[sort] > b[sort] ? -1 : 1
          }
        })
      }
      let total = result.length;

      result = result.filter((o, i) => {
        return i >= (pageNo - 1) * pageSize && i < pageNo * pageSize;
      })

      resolve({
        code: 0,
        data: {
          list: result.map(({username,level,lastLoginAt})=>({username,level,lastLoginAt})),
          pageNo: +pageNo,
          pageSize: +pageSize,
          total
        }
      });
    })
  }
}

let logDao = {
  add(log) {
    return new Promise((resolve, reject) => {
      let l = logs[logs.length - 1] || {
        id: 0
      };

      logs.push({
        ...log,
        id: l.id + 1
      });

      resolve({
        code: 0
      });
    })
  },
  search({
    raid,
    player,
    dateRange,
    type,
    mode,
    pageNo = 1,
    pageSize = 20
  }) {
    return new Promise((resolve, reject) => {
      let result = logs.filter(o => {
        return (!raid || o.raid == raid) &&
          (!player || o.player == player) &&
          (!dateRange[0] || o.time >= dateRange[0]) &&
          (!dateRange[1] || o.time < dateRange[1]) &&
          o.scoreType == type &&
          (mode !== 'USE' || o.score < 0)
      }).sort((a, b) => {
        return new Date(b.time) - new Date(a.time)
      })

      let total = result.length;

      result = result.filter((o, i) => {
        return i >= (pageNo - 1) * pageSize && i < pageNo * pageSize;
      })

      resolve({
        code: 0,
        data: {
          list: result,
          pageNo: +pageNo,
          pageSize: +pageSize,
          total
        }
      });
    })
  }
};

let playerDao = {
  add(player) {
    return new Promise((resolve, reject) => {
      if (players.some(o => o.name == player.name)) {
        reject({
          code: 3005,
          msg: '角色已存在'
        });
      } else {
        players.push(player);

        resolve({
          code: 0
        });
      }
    })
  },
  update(player, withoutLink = false) {
    return new Promise((resolve, reject) => {
      let p = players.find(o => o.name == player.name);
      if (p) {
        if (player.linkedTo) {
          let t = players.find(o => o.name == player.linkedTo);
          if (t && t.linkedTo) {
            reject({
              code: 3,
              msg: `要关联的角色已经是[${t.linkedTo}]的小号，不允许链式关联！`
            });
            return;
          }

          if (players.some(o => o.linkedTo == p.name)) {
            reject({
              code: 3,
              msg: `当前角色已经是某些角色的主号，不能直接变为小号！`
            });
            return;
          }
        }
        p.race = player.race || p.race;
        p.class = player.class || p.class;
        p.level = player.level;
        if (!withoutLink) {
          p.linkedTo = player.linkedTo;
        }
      }
      resolve({
        code: 0
      })
    })
  },
  changeScore(playerNames, {
    raid,
    scoreType,
    reason,
    reasonNote,
    itemID,
    itemName,
    color,
    clazz,
    subClass,
    count,
    boss,
    zone,
    note,
    time,
    score
  }) {
    return new Promise((resolve, reject) => {
      playerNames.forEach(name => {
        let player = players.find(o => o.name == name);
        if (player) {
          let from = ''
          if (player.linkedTo) { //该字段有值代表是小号，积分的增减会体现到大号上去
            from = player.name;
            player = players.find(o => o.name == player.linkedTo)
          }

          player.scores[scoreType] += score;
          logDao.add({
            raid,
            player: player.name,
            from,
            score,
            scoreType,
            reason,
            reasonNote,
            itemName,
            itemID,
            color,
            class: clazz,
            subClass,
            count,
            boss,
            zone,
            note,
            time: time || moment().format(DT_FORMAT)
          })
        }
      })

      resolve({
        code: 0
      });
    })
  },
  search({
    name,
    classes,
    pageNo = 1,
    pageSize = 20,
    sort,
    order
  }) {
    const instances = [
      // ['MC','熔火之心'],
      // ['BWL','黑翼之巢'],
      ['TAQ','安其拉神殿'],
      ['NAXX','纳克萨玛斯'],
      ['TAQ2','安其拉神殿'],
      ['NAXX2','纳克萨玛斯']
    ];

    let rds = {};
    instances.forEach(instance => {
      rds[instance[0]] = raids.filter(r => r.zone === instance[1]);
    })

    return new Promise((resolve, reject) => {
      let result = players.map(o => {
        let attendanceRatio = {};
        let attendanceDistribution = {};
        let classes = {};
        let parent = null;
        if(o.linkedTo){
          instances.forEach(instance => {
            attendanceRatio[instance[0]] = {
              appears: 0,
              total: 0,
              ratio: 0
            };
          })
          parent = players.find(p => p.name === o.linkedTo)

          attendanceRatio.TOTAL = {
            appears: 0,
            total: 0,
            ratio: 0
          }
        } else {
          let ps = players.filter(p => {
            if(o.linkedTo){
              return p.linkedTo === o.linkedTo || p.name === o.linkedTo
            }else{
              return p.linkedTo === o.name || p.name === o.name
            }
          }).sort((a,b) => a.joinTime<b.joinTime?-1:1)

          let playerNames = ps.map(p => p.name);
          instances.forEach(instance => {
            let rs = rds[instance[0]].filter(r => {
              return r.start > ps[0].joinTime
            });

            let appears = rs.filter(r => {
              return playerNames.some(p => Object.keys(r.players).includes(p))
            }).length;

            let total = rs.length;

            attendanceRatio[instance[0]] = {
              appears,
              total,
              ratio: (total === 0 ? 0 : parseInt(appears / total * 100))
            };
          })

          attendanceRatio.TOTAL = {
            appears: instances.reduce((last,instance) => last + attendanceRatio[instance[0]].appears, 0),
            total: instances.reduce((last,instance) => last + attendanceRatio[instance[0]].total, 0)
          }

          attendanceRatio.TOTAL.ratio = parseInt(attendanceRatio.TOTAL.appears / attendanceRatio.TOTAL.total * 100)

          instances.forEach(instance => {
            let rs = rds[instance[0]].filter(r => {
              return r.start > ps[0].joinTime
            });

            attendanceDistribution[instance[0]] = {}
            ps.forEach(p => {
              let appears = rs.filter(r => {
                return r.players[p.name]
              }).length;

              attendanceDistribution[instance[0]][p.name] = appears
            })
          })

          attendanceDistribution.TOTAL = {}
          instances.forEach(instance => {
            let ins = instance[0]
            attendanceDistribution.TOTAL[ins] = Object.values(attendanceDistribution[ins]).reduce((l,n)=>l+n,0)
          })

          ps.forEach(p=>{
            classes[p.name] = p.class;
          })
        }

        return {
          ...o,
          parent,
          attendanceDistribution,
          attendanceRatio,
          classes
        }
      }).filter(o => {
        // console.log(`find [${name}] in [${o.name}]---${o.name.indexOf(name)}`)
        // console.log(`[${o.name}]---${o.name.indexOf(name)>-1} -- ${!classes || classes.indexOf(o.class)>-1}`)
        return (!name || o.name.indexOf(name) > -1) && (!classes || classes.indexOf(o.class) > -1)
      })

      let isAscending = order == '' || order == 'ascending'
      if (sort) {
        if(sort === 'joinTime'){
          result = result.sort((a, b) => {
            return (isAscending ? 1 : -1) * (new Date(a.joinTime) - new Date(b.joinTime))
          })
        }else{
          if(sort.indexOf('attendanceRatio.')===0) {
            let type = sort.replace('attendanceRatio.','');
            result = result.sort((a, b) => {
              if(a.linkedTo && !b.linkedTo){
                return isAscending ? -1 : 1
              }else{
                return (isAscending ? 1 : -1) * (a.attendanceRatio[type].ratio - b.attendanceRatio[type].ratio)
              }
            })
          }else{
            result = result.sort((a, b) => {
              return (isAscending ? 1 : -1) * (a.scores[sort] - b.scores[sort])
            })
          }
        }
      }


      let total = result.length;
      result = result.filter((o, i) => {
        return i >= (pageNo - 1) * pageSize && i < pageNo * pageSize;
      })

      resolve({
        code: 0,
        data: {
          list: result,
          pageNo: +pageNo,
          pageSize: +pageSize,
          total
        }
      });
    })
  },
  allNames() {
    return new Promise((resolve, reject) => {
      resolve({
        code: 0,
        data: {
          list: players.map(o => o.name),
        }
      });
    })
  },
  allClasses() {
    return new Promise((resolve, reject) => {
      resolve({
        code: 0,
        data: {
          list: players.map(o =>({
            name: o.name,
            class: o.class,
            level: o.level,
            race: o.race,
            linkedTo: o.linkedTo
          })),
        }
      });
    })
  },
  playerExists(name) {
    return new Promise((resolve, reject) => {
      resolve({
        code: 0,
        data: {
          exists: players.some(o => o.name == name)
        }
      })
    })
  },
  exists(name) {
    return players.some(o => o.name == name)
  },
  playerRelation() {
    return players.map(o => ({
      name: o.name,
      race: o.race,
      class: o.class,
      linkedTo: o.linkedTo
    }))
  },
  find(name) {
    return players.find(o => o.name == name)
  },
  getBoardMsg() {
    return boardMsg
  },
  setBoardMsg(msg){
    boardMsg = msg;
    fs.writeFileSync(path.join(__dirname, 'board.txt'), msg)
  },
  remove(name) {
    //删除该角色的日志
    for(let i = 0; i < logs.length; i++){
      if(logs[i].player === name){
        logs.splice(i--,1);
      }
    }
    //如果该角色有关联的小号，将关联小号的linkedTo置空
    players.forEach(p=>{
      if(p.linkedTo == name){
        p.linkedTo = null
      }
    })
    //删除角色
    for(let i = 0; i < players.length; i++){
      if(players[i].name === name){
        players.splice(i,1)
        break;
      }
    }
  }
};

let raidDao = {
  add(raid) {
    return new Promise((resolve, reject) => {
      if (raids.some(o => o.key == raid.key)) {
        reject({
          code: 4005,
          msg: '活动已存在'
        });
      } else {
        raids.push(raid);
        resolve({
          code: 0
        });
      }
    })
  },
  delete(key) {
    return new Promise((resolve, reject) => {
      for (let i = 0; i < raids.length; i++) {
        if (raids[i].key == key) {
          raids.splice(i, 1);
          resolve({
            code: 0,
            msg: '操作成功',
            data: moment().format(DT_FORMAT)
          });
          return;
        }
      }
      reject({
        code: 2003,
        msg: '没有找到记录'
      });
    })
  },
  search({
    key,
    dateRange = [],
    pageNo = 1,
    pageSize = 10
  }) {
    return new Promise((resolve, reject) => {
      let result = raids.filter(o => {
        return (!key || o.key.indexOf(key) >= 0) &&
          (!dateRange[0] || o.start >= dateRange[0]) &&
          (!dateRange[1] || o.start < dateRange[1])
      }).sort((a, b) => {
        return new Date(b.start) - new Date(a.start)
      })

      let total = result.length;
      result = result.filter((o, i) => {
        return i >= (pageNo - 1) * pageSize && i < pageNo * pageSize;
      })

      resolve({
        code: 0,
        data: {
          list: result.map(o => ({
            key: o.key,
            zone: o.zone,
            start: o.start,
            end: o.end,
            players: Object.keys(o.players)
              .filter(p=>players.some(o=>o.name==p))
              .map(o => ({
                name: o,
                class: players.find(p => p.name == o).class
              })),
            hasFirstDown: o.hasFirstDown || false
          })),
          pageNo: +pageNo,
          pageSize: +pageSize,
          total
        }
      });
    })
  },
  detail(key) {
    return new Promise((resolve, reject) => {
      let raid = raids.find(o => o.key == key)
      if (raid) {
        resolve({
          code: 0,
          data: raid
        })
      } else {
        reject({
          code: 3,
          msg: '没有查询到该活动'
        })
      }
    })
  },
  info(sourceName) {
    return new Promise((resolve, reject) => {
      fs.readFile(path.join(__dirname,'..','public','dkpFiles','sources',sourceName),{
        encoding: 'utf8'
      }, function(err, result){
        if(err){
          resolve({
            code: -1,
            msg: '没有找到记录'
          });
        }else{
          resolve({
            code: 0,
            data: JSON.parse(result)
          })
        }
      })      
    })    
  }
};

let getUserByReq = function (req) {
  var token = req.headers.authorization;
  return token && admins.filter(o => o.token == token)[0]
}

module.exports = {
  adminDao,
  logDao,
  playerDao,
  raidDao,
  save,
  getUserByReq,
  backupList,
  backup,
  restore
}